/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "DoradoApiOperator.h"
#include <chrono>
#include <sstream>
#include <ctime>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "error.h"
#include "system/System.hpp"
#include "common/Utils.h"
#include "common/JsonUtils.h"
#include "common/Structs.h"
#include "common/Timer.h"
#include "volume_handlers/common/DiskCommDef.h"
#include "DiskScannerHandler.h"

using namespace chrono;
namespace {
const std::string MODULE_NAME = "DoradoApiOperator";
const std::string HOSTNAME = "hostName";
const std::string LUNNAME = "lunName";
const std::string LUNGROUPNAME = "lunGroupName";
const std::string illegalChar = ".-:/@?+()<>$!~#%^*&|,;";
constexpr int HOST_TYPE = 21;
constexpr int LINUX_TYPE = 0;
constexpr int DORADO_LINK_TYPE = 2;
const int32_t DORADO_NUMBER_TWO = 2;
const int32_t DORADO_DEFAULT_PARENT_TYPE = 40;
const int32_t FORWARD = 2;
const uint32_t DEFAULT_CHUNKSIZE = 65536;
}

namespace VirtPlugin {
/**
 *  @brief 初始化映射集合：
 *          1、创建Iscsi启动器、主机
 *          2、将启动器添加到主机
 *          3、创建主机-Lun组映射视图，LUN添加到LUN组
 *
 *  @return 成功返回 SUCCESS，出错返回 Module::FAILED
 */
int32_t DoradoApiOperator::CreateMappingSet(const std::string& objId, MO_TYPE objType)
{
    INFOLOG("Start to create mapping set.");
    if (InitMappingNameInfo() != Module::SUCCESS) {
        return Module::FAILED; /* Log error inside. */
    }
    HostMO hostMO;
    int32_t iRet = CreateInitiatorAndHost(m_hostName, hostMO);
    if (iRet != Module::SUCCESS) {
        return Module::FAILED;
    }
    if (CreateLunGroupAndAddObj(m_lunGroupName, objId, objType) != Module::SUCCESS) {
        return Module::FAILED; /* Log error inside. */
    }
    {
        std::lock_guard<std::mutex> guard(m_mutexMappingView);
        iRet = QueryHostLunGroupMapping(m_hostName, m_lunGroupName);
        if (iRet != Module::SUCCESS) {
            iRet = CreateHostLunGroupMapping(m_hostName, m_lunGroupName); // 创建主机-Lun组映射视图
            if (iRet != Module::SUCCESS) {
                return Module::FAILED;
            }
        }
    }
    INFOLOG("Create mapping set success.");
    return Module::SUCCESS;
}

/**
 *  @brief 取消映射集合：从LUN组中移除LUN，不删映射视图（可能其他LUN还在使用）
 *
 *  @return 成功返回 SUCCESS，出错返回 Module::FAILED
 */
int32_t DoradoApiOperator::DeleteMappingSet(const std::string& objId, MO_TYPE objType)
{
    INFOLOG("Start to delete mapping set(lun group - lun), obj id:%s", objId.c_str());
    int iRet = RemoveObjFromLunGroupExp(m_lunGroupName, objId, objType);
    if (iRet != Module::SUCCESS) {
        ERRLOG("Remove obj from lun group failed, lun group: %s, obj id: %s, obj type %d",
            m_lunGroupName.c_str(), objId.c_str(), objType);
        return iRet;
    }
    INFOLOG("Delete mapping set success(lun group - lun), obj id:%s", objId.c_str());
    return Module::SUCCESS;
}

int32_t DoradoApiOperator::CreateHostLunMapping(const std::string &hostName, const std::string &lunName)
{
    INFOLOG("Create mapping, host name:%s, lun name:%s", hostName.c_str(), lunName.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    jsonReq["hostName"] = hostName;
    jsonReq["lunName"] = lunName;
    Json::FastWriter jsonWriter;
    req.method = "POST";
    req.url = "api/v2/mapping";
    req.body = jsonWriter.write(jsonReq);
    Json::Value data;
    std::string errorDes;
    int errorCode = -1;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::LUN_HOST_MAPPING_EXIST ||
        errorCode == ApiErrorCode::LUNGROUP_HOST_MAPPING_EXIST)) {
        return Module::SUCCESS;
    } else {
        ERRLOG("Create host mapping failed, error:%d, des:%s", errorCode, errorDes.c_str());
        return Module::FAILED;
    }
}

int32_t DoradoApiOperator::CreateHostLunGroupMapping(const std::string &hostName, const std::string &lunGroupName)
{
    INFOLOG("Create mapping, host name:%s, lun group name:%s", hostName.c_str(), lunGroupName.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    jsonReq["hostName"] = hostName;
    jsonReq["lunGroupName"] = lunGroupName;
    Json::FastWriter jsonWriter;
    req.method = "POST";
    req.url = "api/v2/mapping";
    req.body = jsonWriter.write(jsonReq);
    Json::Value data;
    std::string errorDes;
    int errorCode = -1;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS
        && (errorCode == Module::SUCCESS || errorCode == ApiErrorCode::LUNGROUP_HOST_MAPPING_EXIST)) {
        return Module::SUCCESS;
    } else {
        ERRLOG("Create host to lun group mapping failed, error:%d, des:%s", errorCode, errorDes.c_str());
        return Module::FAILED;
    }
}

int32_t DoradoApiOperator::QueryHostLunGroupMapping(const std::string &hostName, const std::string &lunGroupName)
{
    INFOLOG("Query host mapping %s to lun group %s", hostName.c_str(), lunGroupName.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    req.method = "GET";
    req.url = "api/v2/mapping?hostName=" + hostName + "&lunGroupName=" + lunGroupName;
    Json::Value data;
    std::string errorDes;
    int errorCode = -1;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty()) {
        return Module::SUCCESS;
    }
    ERRLOG("Query host lun group mapping failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return Module::FAILED;
}

int32_t DoradoApiOperator::QueryHostLunMapping(const std::string &hostName, const std::string &lunName)
{
    INFOLOG("Query host mapping %s to lun %s", hostName.c_str(), lunName.c_str());
    Module::HttpRequest req;
    Json::Value jsonReq;
    req.method = "GET";
    req.url = "api/v2/mapping?hostName=" + hostName + "&lunName=" + lunName;
    Json::Value data;
    std::string errorDes;
    int errorCode = Module::FAILED;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && errorCode == Module::SUCCESS && !data.empty()) {
        return Module::SUCCESS;
    }
    ERRLOG("Query host lun mapping failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return Module::FAILED;
}

int32_t DoradoApiOperator::DeleteHostLunMapping(const std::string &hostName, const std::string &lunName)
{
    INFOLOG("Start delete host mapping %s to lun %s", hostName.c_str(), lunName.c_str());
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = "api/v2/mapping?hostName=" + hostName + "&lunName=" + lunName;
    Json::Value data;
    std::string errorDes;
    int errorCode = -1;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS ||
        errorCode == ApiErrorCode::LUN_HOST_MAPPING_NOTEXIST ||
        errorCode == ApiErrorCode::LUNGROUP_HOST_MAPPING_NOTEXIST)) {
        return Module::SUCCESS;
    }
    ERRLOG("Delete host mapping failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return Module::FAILED;
}

int32_t DoradoApiOperator::DeleteHostLunGroupMapping(const std::string &hostName, const std::string &lunGroupName)
{
    INFOLOG("Start delete host mapping %s to lun group %s", hostName.c_str(), lunGroupName.c_str());
    Module::HttpRequest req;
    req.method = "DELETE";
    req.url = "api/v2/mapping?hostName=" + hostName + "&lunGroupName=" + lunGroupName;
    Json::Value data;
    std::string errorDes;
    int errorCode = -1;
    int iRet = SendRequest(req, data, errorDes, errorCode);
    if (iRet == Module::SUCCESS && (errorCode == Module::SUCCESS ||
        errorCode == ApiErrorCode::LUN_HOST_MAPPING_NOTEXIST ||
        errorCode == ApiErrorCode::LUNGROUP_HOST_MAPPING_NOTEXIST)) {
        return Module::SUCCESS;
    }
    ERRLOG("Delete host lun group mapping failed, error:%d, des:%s", errorCode, errorDes.c_str());
    return Module::FAILED;
}

int32_t DoradoApiOperator::NameLegalization(std::string &name)
{
    std::vector<std::string>::iterator iter;
    int begin = -1;
    std::string result = "";
    for (int i = 0; i < name.length(); i++) {
        if ((begin = illegalChar.find(name[i], begin + 1)) == std::string::npos) {
            result = result + name[i];
        }
    }
    name = result;
    return Module::SUCCESS;
}

int32_t DoradoApiOperator::GetObjName(const std::string& objId, MO_TYPE objType, std::string &objName)
{
    if (objId.empty()) {
        ERRLOG("Get obj name failed, obj id is empty.");
        return Module::FAILED;
    }
    if (objType == MO_SNAPSHOT) {
        SnapshotMO snapshotMO;
        std::string errorDes;
        if (GetSnapShotByID(objId, snapshotMO, errorDes) != Module::SUCCESS) {
            ERRLOG("Get snapshot info failed, id:%s", objId.c_str());
            return Module::FAILED;
        }
        objName = snapshotMO.m_name;
    } else if (objType == MO_LUN) {
        LunMO lunMo;
        std::string errorDes;
        if (GetLunByID(objId, lunMo, errorDes) != Module::SUCCESS) {
            ERRLOG("Get lun info failed, id:%s", objId.c_str());
            return Module::FAILED;
        }
        objName = lunMo.m_name;
    }
    if (objName.empty()) {
        ERRLOG("Get obj name failed, obj type:%d", objType);
        return Module::FAILED;
    }
    return Module::SUCCESS;
}
}